package edu.arizona.sista.learn.model.interest;

import java.io.File;
import java.util.Arrays;
import java.util.Scanner;

import edu.arizona.sista.learn.model.Interval;
import edu.arizona.sista.learn.model.holding.Person;
import edu.arizona.sista.learn.model.holding.Scene;
import edu.arizona.sista.learn.model.inferences.Predicate;
import edu.arizona.sista.learn.util.Utils;

/**
 * A class with static methods to determine when a person is walking forwards, backwards or stays at the same abstract location. 
 *
 */

public class Walking {
	public static int SAMPLE_SIZE = 10;
	private static int LAST_LOCATION_ID = 1;
	public static int FORWARDS = 0, BACKWARDS = 1, MOTIONLESS = 2; 

	public static void computeWalkingEvents(Scene scene, File gazeFolder){
		//if movement vector is opposite to gaze, then the person is walking backwards
		
		//load gaze vectors (location and coordinate)
		loadGaze(scene, gazeFolder);
		
		for (Person p : scene._people){
			if (p._gaze != null){
				//Forwards
				boolean[] forwardsFluent = computeWalkingFluent(p, FORWARDS);
				int start = -1;
				int i = 0;
				while(i < forwardsFluent.length){
					while(i < forwardsFluent.length && !forwardsFluent[i]) i++;
					start = i;
					while(i < forwardsFluent.length && forwardsFluent[i]) i++;
					if (i > start + 1) scene.addEvent(new WalkingForwards(new Interval(start * SAMPLE_SIZE + 1, i * SAMPLE_SIZE), p));
				}

				//Backwards
				boolean[] backwardsFluent = computeWalkingFluent(p, BACKWARDS);
				start = -1;
				i = 0;
				while(i < backwardsFluent.length){
					while(i < backwardsFluent.length && !backwardsFluent[i]) i++;
					start = i;
					while(i < backwardsFluent.length && backwardsFluent[i]) i++;
					if (i > start + 1) scene.addEvent(new WalkingBackwards(new Interval(start * SAMPLE_SIZE + 1, i * SAMPLE_SIZE), p));
				}
				
				//Motionless
				boolean[] motionlessFluent = computeWalkingFluent(p, MOTIONLESS);
				start = -1;
				i = 0;
				while(i < motionlessFluent.length){
					while(i < motionlessFluent.length && !motionlessFluent[i]) i++;
					start = i;
					while(i < motionlessFluent.length && motionlessFluent[i]) i++;
					if (i > start + 1){
						Interval t = new Interval(start * SAMPLE_SIZE + 1, i * SAMPLE_SIZE);
						String s1 = "t_"  + t.start() + "-" + t.end();
						scene.addPredicate(new Predicate("isa(" + s1 + ", TimeInterval)"));
						scene.addPredicate(new Predicate("isa(l_" + LAST_LOCATION_ID + ", Location)"));
						scene.addPredicate(new Predicate("holdsIn(" + s1 + ", locatedAt(" + p.getVariableName() + ", l_" + LAST_LOCATION_ID + "))"));
						LAST_LOCATION_ID++;
					}
				}
				
			}
		}
		
	}

	private static boolean[] computeWalkingFluent(Person p, int motion) {
		int n = (p.frames() - 1) / SAMPLE_SIZE;
		boolean[] fluent = new boolean[n];
		for(int i = 0; i < n; i++){
			int indexMax = SAMPLE_SIZE - 1;
			if (i * SAMPLE_SIZE + SAMPLE_SIZE >= p.frames()) indexMax = p.frames() - i * SAMPLE_SIZE - 1;
			
			//movement vector
			int beginIndex = i * SAMPLE_SIZE; 
			int endIndex = i * SAMPLE_SIZE + indexMax;
			while(beginIndex < i * SAMPLE_SIZE + indexMax && Double.isInfinite(p._gaze[beginIndex][0])) beginIndex++;
			while(endIndex > beginIndex && Double.isInfinite(p._gaze[endIndex][0])) endIndex--;
			if (beginIndex == endIndex) continue;
			
			double[] mv = new double[]{
				p._gaze[endIndex][0] - p._gaze[beginIndex][0],
				p._gaze[endIndex][1] - p._gaze[beginIndex][1]
			};
			double mvNorm2 = mv[0] * mv[0] + mv[1] * mv[1];
			
			//mean gaze vector
			double[] mg = new double[2];
			for(int j = 0; j <= indexMax; j++){
				double x = p._gaze[i * SAMPLE_SIZE + j][3];
				double y = p._gaze[i * SAMPLE_SIZE + j][4];
				if (Double.isInfinite(x) || Double.isInfinite(y)) continue;
				mg[0] += x;
				mg[1] += y;
			}
			
			//if the angle between both vectors is more than pi / 2 or less than -pi / 2
			//and if the speed is higher than some threshold that is dependent on the size of the hand of the person
			//then the person is walking backwards
			double a = mv[0] * mg [0] + mv[1] * mg [1];
			double headArea = p.getHeadArea(); 
			boolean fastEnough = mvNorm2 > headArea * SAMPLE_SIZE * .0000005;
			if (motion == FORWARDS) {
				if (a >= 0 && fastEnough) fluent[i] = true;
			}
			else if (motion == BACKWARDS){
				if (a <= 0 && fastEnough) fluent[i] = true;
			}
			else if (!fastEnough) fluent[i] = true;
		}
		return fluent;
	}

	private static void loadGaze(Scene scene, File gazeFolder) {
		try {
			File[] personFiles = gazeFolder.listFiles();
			if (personFiles != null){
                for (int i = 0; i < personFiles.length; i++) {
                	if (!personFiles[i].getName().contains(".txt")) continue;
                	Utils.ActorName actorName = Utils.getActorNameFromTrackID(scene._name, personFiles[i].getName());
                	Person p = scene.getPerson(actorName);
                	int n = p.frames();
                	double[][] gaze = new double[n][6]; //frame,vector (location + coordinates)
                	for(int x = 0; x < n; x++) for(int y = 0; y < 6; y++) gaze[x][y] = Double.POSITIVE_INFINITY; 
                	Scanner gazeScanner = new Scanner(personFiles[i]);
        			while (gazeScanner.hasNextLine()) {
        				String[] tokens = gazeScanner.nextLine().split(" +");

        				//frame, locX, locY, locZ, dirX, dirY, dirZ
        				int frame = Integer.parseInt(tokens[0]);
        				for(int j = 0; j < 6; j++) gaze[frame - 1][j] = Double.parseDouble(tokens[j + 1]);
        			}
        			
        			//interpolation for missing frames
        			//Utils.interpolate(gaze);
        			
        			p.setGaze(gaze);
                }
            } else {
            	System.err.println("Error while reading " + personFiles + ".");
            }
            
		} catch (Exception e) {
			System.out.println("Exception while reading file: " + e);
			e.printStackTrace();
		}
		
	}

}
